package com.linln.admin.sync.service.impl;

import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linln.admin.app.constants.AppConstants;
import com.linln.admin.app.domain.AppVersion;
import com.linln.admin.app.domain.VersionMember;
import com.linln.admin.app.repository.VersionRepository;
import com.linln.admin.app.service.impl.VersionServiceImpl;
import com.linln.admin.sync.domain.FlowNacos;
import com.linln.admin.sync.domain.NacosDefaultConf;
import com.linln.admin.sync.repository.FlowNacosRepository;
import com.linln.admin.sync.repository.NacosDefaultConfRepository;
import com.linln.admin.sync.service.FlowNacosService;
import com.linln.common.data.PageSort;
import com.linln.common.enums.StatusEnum;
import com.linln.common.utils.HttpServletUtil;
import com.linln.component.shiro.ShiroUtil;
import com.linln.component.thymeleaf.utility.DictUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.*;
import java.io.StringReader;
import java.util.*;

/**
 * @author taofucheng
 * @date 2021/11/26
 */
@Service
public class FlowNacosServiceImpl implements FlowNacosService {

    @Autowired
    private FlowNacosRepository flowNacosRepository;
    @Resource
    private NacosDefaultConfRepository defaultConfRepository;
    @Autowired
    private VersionRepository versionRepository;

    /**
     * 根据ID查询数据
     *
     * @param id 主键ID
     */
    @Override
    public FlowNacos getById(Long id) {
        return flowNacosRepository.findById(id).orElse(null);
    }

    /**
     * 获取分页列表数据
     *
     * @param example 查询实例
     * @return 返回分页数据
     */
    @Override
    public Page<FlowNacos> getPageList(Example<FlowNacos> example) {
        // 创建分页对象
        Pageable page = PageSort.pageRequest();
        FlowNacos flowNacos = example.getProbe();

        return flowNacosRepository.findAll(new Specification<FlowNacos>() {

            public Predicate toPredicate(Root<FlowNacos> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> preList = new ArrayList<>();
                if (StringUtils.isNotBlank(flowNacos.getTitle())) {
                    preList.add(cb.like(root.get("title").as(String.class), "%" + flowNacos.getTitle() + "%"));
                }
                if (StringUtils.isNotBlank(flowNacos.getData_id())) {
                    preList.add(cb.like(root.get("data_id").as(String.class), "%" + flowNacos.getData_id() + "%"));
                }
                if (StringUtils.isNotBlank(flowNacos.getAdd_content())) {
                    preList.add(cb.or(cb.like(root.get("change_content").as(String.class), "%" + flowNacos.getChange_content() + "%"),
                            cb.like(root.get("add_content").as(String.class), "%" + flowNacos.getAdd_content() + "%")));
                }
                if (StringUtils.isNotBlank(flowNacos.getAdd_flag())) {
                    preList.add(cb.equal(root.get("add_flag").as(String.class), flowNacos.getAdd_flag()));
                }
                if (flowNacos.getVersion_id() != null) {
                    preList.add(cb.equal(root.get("version_id").as(Long.class), flowNacos.getVersion_id()));
                }else{
                    if (!ShiroUtil.isAdmin()) {//不是管理员，只能查看自己创建信息
                        Subquery<VersionMember> subquery = query.subquery(VersionMember.class);
                        //子查询 要查询哪个实体
                        Root<VersionMember> subRoot = subquery.from(VersionMember.class);
                        //子查询要查询哪个字段
                        subquery = subquery.select(subRoot.get("version_id"));
                        subquery.where(subRoot.get("user_id").in(ShiroUtil.getSubject().getId()));
                        //将子查询与父查询和到一起
                        preList.add(cb.and(root.get("version_id").in(subquery)));
                    }
                }
                try {
                    String published = HttpServletUtil.getParameter("published", "");
                    if (StringUtils.isNotBlank(published)) {
                        //创建子查询，返回的类型
                        Subquery<AppVersion> subquery = query.subquery(AppVersion.class);
                        //子查询 要查询哪个实体
                        Root<AppVersion> subRoot = subquery.from(AppVersion.class);
                        //子查询要查询哪个字段
                        subquery = subquery.select(subRoot.get("id"));
                        subquery.where(subRoot.get("published").in(published));
                        //将子查询与父查询和到一起
                        preList.add(cb.and(root.get("version_id").in(subquery)));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                Predicate[] pres = new Predicate[preList.size()];
                return query.where(preList.toArray(pres)).getRestriction();
            }

        }, page);
    }

    /**
     * 保存数据
     *
     * @param flowNacos 实体对象
     */
    @Override
    public FlowNacos save(FlowNacos flowNacos) {
        //下面的逻辑注释掉的原因：因为有时候一个版本中，会有多个需求，每个需求由不同的开发各自开发、配置，如果维护同一份配置，容易互相覆盖。
        // 所以，允许各开发之间自己维护自己的配置，最后由系统自动合并，然后给各个开发确认汇总的配置。
//        FlowNacos where = new FlowNacos();
//        where.setData_id(flowNacos.getData_id());
//        where.setGroup_id(flowNacos.getGroup_id());
//        where.setVersion_id(flowNacos.getVersion_id());
//        if (flowNacos.getId() == null) {
//            if (flowNacosRepository.count(Example.of(where)) > 0) {
//                throw new ResultException(400, "该配置文件已经存在！");
//            }
//        } else {
//            List<FlowNacos> records = flowNacosRepository.findAll(Example.of(where));
//            if (records != null) {
//                for (FlowNacos record : records) {
//                    if (record.getId().longValue() != flowNacos.getId().longValue()) {//如果存在与当前记录不同id的，表示修改了dataId或groupId，同时与已经存在的冲突了
//                        throw new ResultException(400, "该配置文件已经存在！");
//                    }
//                }
//            }
//        }
        if (isPublished(flowNacos)) {
            throw new IllegalArgumentException("已经发布的SQL不能修改！");
        }
        flowNacos.setCreate_id(ShiroUtil.getSubject().getId());
        flowNacos.setCreate_time(new Date());
        flowNacos.setSynced("");//一旦有变更，则就可以重新同步。
        return flowNacosRepository.save(flowNacos);
    }

    /**
     * 状态(启用，冻结，删除)/批量状态处理
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(StatusEnum statusEnum, List<Long> idList) {
        return flowNacosRepository.updateStatus(statusEnum.getCode(), idList) > 0;
    }

    @Override
    public void sync(String combinePubEnv) throws NacosException {
        //根据环境，生成对应环境的nacosConfig连接
        if (StringUtils.isBlank(combinePubEnv)) {
            throw new IllegalArgumentException("请选择要同步的环境");
        }

        String nacosConf = DictUtil.keyValue("ENV_NACOS", combinePubEnv);
        if (StringUtils.isBlank(nacosConf)) {
            throw new IllegalArgumentException("没有对应环境的nacos连接配置，无法进行同步");
        }
        String[] parts = nacosConf.split("\\$", 3);
        if (parts.length != 3) {
            throw new IllegalArgumentException("对应环境的nacos连接配置格式不正确（IP,port,namespace），无法进行同步");
        }

        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, parts[0].trim() + ":" + parts[1].trim());
        properties.put(PropertyKeyConst.NAMESPACE, parts[2].trim());
        ConfigService configService = ConfigFactory.createConfigService(properties);

        try {
            NacosDefaultConf defaultConf = defaultConfRepository.findFirstByEnv_type(combinePubEnv);
            if (defaultConf == null || StringUtils.isBlank(defaultConf.getContent())) {
                return;
            }
            Properties defaultPro = new Properties();
            try {
                defaultPro.load(new StringReader(defaultConf.getContent()));
            } catch (Exception e) {
                e.printStackTrace();
            }

            //查询所有待同步的记录
            ObjectMapper om = new ObjectMapper();
            int page = 0;
            while (true) {
                Page<FlowNacos> lines = flowNacosRepository.findAll(PageRequest.of(page, 10, Sort.Direction.ASC, "id"));
                if (!lines.hasContent()) {
                    break;
                }
                for (FlowNacos nacos : lines) {
                    if (!isPublished(nacos)) {
                        continue;//还没发布的，不可以同步
                    }
                    List<String> synced = null;//已经向哪些环境同步过
                    try {
                        synced = om.readValue(nacos.getSynced(), new TypeReference<List<String>>() {
                        });
                    } catch (Exception e) {
                        synced = new ArrayList<>();
                    }
                    if (synced.contains(combinePubEnv)) {//如果该环境已经同步过，则不再同步
                        continue;
                    }
                    synced.add(combinePubEnv);

                    //从nacos获取信息
                    String content = configService.getConfig(nacos.getData_id(), nacos.getGroup_id(), 10_000);
                    Map<String, String> result = new LinkedHashMap<>();
                    toProperties(result, content);
                    toProperties(result, nacos.getAdd_content());
                    toProperties(result, nacos.getChange_content());
                    //把最新的信息写到对应的nacos中
                    VersionServiceImpl.replaceWithDefault(combinePubEnv, defaultPro, result);
                    configService.publishConfig(nacos.getData_id(), nacos.getGroup_id(), toString(result), "properties");

                    // 更新状态
                    try {
                        nacos.setSynced(om.writeValueAsString(synced));
                    } catch (Exception e) {
                    }
                    flowNacosRepository.saveAndFlush(nacos);
                }
                ++page;
            }
        } finally {
            try {
                configService.shutDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void toProperties(Map<String, String> result, String content) {
        if(StringUtils.isBlank(content)){
            return;
        }
        for (String line : content.split("\n")) {
            String[] parts = line.split("=", 2);
            String key = parts[0].trim();
            String value = parts.length == 2 ? parts[1].trim() : "";
            if (line.startsWith("#")) {//注释保留
                key = line;
                value = "";
            }
            result.put(StringUtils.defaultIfBlank(key, "# " + result.size()), value);//空行也留着
        }
    }

    /**
     * 将Properties转换成文本字符串的格式
     *
     * @param props 内容
     * @return
     */
    private String toString(Map<String, String> props) {
        StringBuilder sb = new StringBuilder();
        if (props == null || props.isEmpty()) {
            return sb.toString();
        }
        for (String key : props.keySet()) {
            if (StringUtils.isBlank(key)) {
                sb.append("\r\n");
            } else if (key.startsWith("#")) {
                sb.append(key);
            } else {
                sb.append(key + "=" + props.getOrDefault(key, "") + "\r\n");
            }
        }

        return sb.toString();
    }

    private boolean isPublished(FlowNacos flow) {
        Optional<AppVersion> version = versionRepository.findById(flow.getVersion_id());
        return version.isPresent() ? AppConstants.YES.equals(version.get().getPublished()) : false;
    }
}